#include <string.h>
#include <assert.h>
#ifndef WIN32
#include <alloca.h>
#else
#include <malloc.h>
#endif

#include "vfs.h"
#include "vnode_map.h"

static void cleanup_path(char *ptr);

static VNodeDir def_root;
static VNodeDir *root = &def_root;
static VNodeDir *cur_dir = &def_root;
static VNodeDirMap real_map;

void set_root(VNodeDir *dir)
{
	root = dir;
	cur_dir = dir;
}

VNodeDir *get_root()
{
	return root;
}

bool change_dir(const char *path)
{
	VNode *node = lookup_path(path);
	if(!node || !node->is_dir()) {
		return false;
	}
	cur_dir = (VNodeDir*)node;
	return true;
}

bool make_dir(const char *path)
{
	char *pathbuf = (char*)alloca(strlen(path) + 1);
	strcpy(pathbuf, path);

	cleanup_path(pathbuf);

	char *name = pathbuf;
	char *lastslash = strrchr(pathbuf, '/');
	if(lastslash) {
		*lastslash = 0;
		name = lastslash + 1;
	}

	VNode *parent_node = lookup_path(pathbuf);
	if(!parent_node || !parent_node->is_dir()) {
		return false;
	}
	VNodeDir *parent = (VNodeDir*)parent_node;

	VNodeDir *newdir = new VNodeDir;
	newdir->set_name(name);

	if(!parent->add_child(newdir)) {
		delete newdir;
		return false;
	}
	return true;
}

VNode *lookup_path(const char *path)
{
	char *pathbuf = (char*)alloca(strlen(path) + 1);
	strcpy(pathbuf, path);

	cleanup_path(pathbuf);

	VNodeDir *vdir = cur_dir;

	if(pathbuf[0] == '/') {
		vdir = root;
		pathbuf++;
	}

	if(vdir) {
		VNode *vnode = vdir->lookup(pathbuf);
		if(vnode) {
			return vnode;
		}
	}

	// fallback in case we don't have any nodes to support init-less operation on the filesystem
	if(!real_map.is_open()) {
		if(!real_map.open(".")) {
			return nullptr;
		}
	}

	// we need to re-create pathbuf from the original because previous lookup might have fucked it up
	strcpy(pathbuf, path);
	cleanup_path(pathbuf);
	return real_map.lookup(pathbuf);
}

static void cleanup_path(char *str)
{
	char *ptr = str;
	while(*ptr) {
		if(*ptr == '\\') *ptr = '/';
		ptr++;
	}

	// remove trailing slashes...
	if(ptr > str && ptr[-1] == '/') {
		ptr[-1] = 0;
	}
}
